﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;

namespace monitor_2_DBwork
{
    public class HardIdentifier : LicHelper
    {
        private const int HashLength = 20;
        private const int QHashLength = HashLength/4;
        private static readonly byte[] StoredAssemblyId;
        private static readonly string CurrentAssemblyId;

        static HardIdentifier()
        {
            StoredAssemblyId = GetStoredAssemblyIdValue();
            CurrentAssemblyId = GetCurrentAssemblyIdValue();
        }

        public static string CheckBinding()
        {
            //var b = new byte[] {0, 0, 0, 0, 0};
            //AppendInfo("pict1", b, 0, 5, true);
            //AppendInfo("pict2", b, 0, 5, true);
            //AppendInfo("pict3", b, 0, 5, true);
            //AppendInfo("pict4", b, 0, 5, true);

            if (!VerifyAssembly())
                return "err:" + DBwork.ResourceManager.GetString("DbToAssemblyBindingError");

            var currentHardValue = GetCurrentHardIdValue();

            //var chk = AppendInfo("pict2", s, 4, 4, true);

            var storedHardValue = GetStoredHardIdValue();

            //Check equals of current and stored values
            if (!CompareByteArrays(currentHardValue, storedHardValue))
                return "err:" + DBwork.ResourceManager.GetString("HardCheckError");

            return "ok";
        }

        

        private static string GetHashString(string input)
        {
            var md5Hash = SHA1.Create();
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            var sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        private static bool VerifyHash(string input, string hash)
        {
            // Hash the input.
            string hashOfInput = GetHashString(input);

            // Create a StringComparer an compare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static bool VerifyHash(byte[] input, byte[] hash)
        {
            // Hash the input.
            var hashOfInput = GetHash(input);

            return !hash.Where((t, i) => hashOfInput[i] != t).Any();
        }

        public static bool SetCurrentHardIdValue(byte[] newId)
        {
            AppendInfo("pict1", newId, 0, 5, true);
            AppendInfo("pict2", newId, 5, 5, true);
            AppendInfo("pict3", newId, 10, 5, true);
            AppendInfo("pict4", newId, 15, 5, true);
            return true;
        }

        //Identify Current Hard
        private static byte[] GetCurrentHardIdValue()
        {
            var sb = GetCurrentHardString();
            return GetHash(sb.ToString());
        }

        public static StringBuilder GetCurrentHardString()
        {
            var sb = new StringBuilder();

            var searcher = new ManagementObjectSearcher
                ("SELECT MediaType, Caption FROM Win32_DiskDrive");

            ManagementObjectCollection information = searcher.Get();
            foreach (ManagementObject obj in information)
            {
                //sb.AppendLine(obj.GetPropertyValue("SerialNumber").ToString());
                int len = 0;
                foreach (PropertyData data in obj.Properties)
                {
                    //sb.AppendLine(string.Format("{0} = {1}", data.Name, data.Value));
                    if (data.Name == "MediaType")
                    {
                        if (!Equals(data.Value, "Fixed hard disk media"))
                        {
                            if (sb.Length >= len)
                                sb.Remove(sb.Length - len, len);
                        }
                    }
                    else if (data.Name == "Caption")
                    {
                        var str = data.Value.ToString();
                        len = str.Length;
                        sb.Append(data.Value);
                    }
                }
            }

            searcher = new ManagementObjectSearcher
                ("SELECT ProcessorId FROM Win32_Processor");

            information = searcher.Get();
            foreach (ManagementObject obj in information)
            {
                foreach (PropertyData data in obj.Properties)
                {
                    sb.Append(data.Value);
                }
            }
            return sb;
        }

        //Current stored hard id
        private static byte[] GetStoredHardIdValue()
        {
            var curpict = DBwork.GetPicture("pict1");
            var ret = new byte[HashLength];
            Buffer.BlockCopy(curpict, curpict.Length - QHashLength, ret, 0, QHashLength);
            curpict = DBwork.GetPicture("pict2");
            Buffer.BlockCopy(curpict, curpict.Length - QHashLength, ret, QHashLength, QHashLength);
            curpict = DBwork.GetPicture("pict3");
            Buffer.BlockCopy(curpict, curpict.Length - QHashLength, ret, QHashLength * 2, QHashLength);
            curpict = DBwork.GetPicture("pict4");
            Buffer.BlockCopy(curpict, curpict.Length - QHashLength, ret, QHashLength * 3, QHashLength);
            return ret;
        }
        
        public static string SetAssemblyId(string path)
        {
            var data = GetHash(CurrentAssemblyId);
            AddPicture("pict5", path, data, 0, HashLength);
            return "ok";
        }

        //Current assembly stored id
        private static byte[] GetStoredAssemblyIdValue()
        {
            var curpict = DBwork.GetPicture("pict5");
            if (curpict.Length < HashLength)
                return new byte[0];
            var ret = new byte[HashLength];
            Buffer.BlockCopy(curpict, curpict.Length - HashLength, ret, 0, HashLength);
            return ret;
        }

        private static string GetCurrentAssemblyIdValue()
        {
            var asm = Assembly.GetAssembly(typeof(monitor_2_DBwork));
            var obj = asm.GetCustomAttributes(typeof (GuidAttribute), true);
            if (obj.Length > 0)
            {
                var attribute = (GuidAttribute) obj[0];
                return attribute.Value;
            }
            return string.Empty;
        }

        private static bool AddPicture(string name, string path)
        {
            var fileData = File.ReadAllBytes(path);
            return DBwork.AddReplacePicture(name, fileData);
        }

        public static bool AddPicture(string name, string path, byte[] dataToAppend, int offset, int count)
        {
            var fileData = File.ReadAllBytes(path);
            var npict = new byte[fileData.Length + count];
            Buffer.BlockCopy(fileData, 0, npict, 0, fileData.Length);
            Buffer.BlockCopy(dataToAppend, offset, npict, fileData.Length, count);
            return DBwork.AddReplacePicture(name, npict);
        }

        public static bool AddPicture(string name, byte[] fileData, byte[] dataToAppend, int offset, int count)
        {
            var npict = new byte[fileData.Length + count];
            Buffer.BlockCopy(fileData, 0, npict, 0, fileData.Length);
            Buffer.BlockCopy(dataToAppend, offset, npict, fileData.Length, count);
            return DBwork.AddReplacePicture(name, npict);
        }

        //Append info to stored picture
        public static bool AppendInfo(string name, byte[] dataToAppend, int offset, int count, bool replace)
        {
            var pict = DBwork.GetPicture(name);
            if (!replace)
            {
                var npict = new byte[pict.Length + count];
                Buffer.BlockCopy(pict, 0, npict, 0, pict.Length);
                Buffer.BlockCopy(dataToAppend, offset, npict, pict.Length, count);
                return DBwork.AddReplacePicture(name, npict);
            }
            Buffer.BlockCopy(dataToAppend, offset, pict, pict.Length - count, count);
            return DBwork.AddReplacePicture(name, pict);
        }

        private static bool VerifyAssembly()
        {
            return CompareByteArrays(StoredAssemblyId, GetHash(CurrentAssemblyId));
        }
    }
}
